﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Commons = NougakudoCompanion.Commons;

namespace NougakuDoController
{
    class Program
    {

        static Program controller = null;

        static void Main(string[] args)
        {
            bool ret = false;

            Logging.Write("NougakudoController:Start.", Logging.LogType.INFO);

            controller = new Program();
            ret = controller.ControllerStart();

            ret = controller.SetupWatcher();
            if (!ret)
            {
                Logging.Write("NougakudoController:Terminate.", Logging.LogType.ERROR);
                return;
            }


            Console.WriteLine("press any key. nougakudo terminate request");
            Console.ReadKey();

            // Never execute code. for local debug only.
            if (controller.initialized)
            {
                string[] apps = controller.launcher.RunningApplicationList;
                controller.StopRequest(apps);
            }
            Console.WriteLine("Press any key");
            Console.ReadKey();

        }

        Commons.Runtime runtime;
        Launcher launcher;
        bool initialized = false;

        FileSystemWatcher stopWatcher;
        FileSystemWatcher startWatcher;
        FileSystemWatcher reStartWatcher;
        FileSystemWatcher controllerStopWatcher;
        System.Timers.Timer downloadTimer;

        bool processing = false;
        object syncObj = new object();

        bool ControllerStart()
        {
            if (!controller.initialized)
            {
                Logging.Write("NougakudoController:Initizalize Start.", Logging.LogType.INFO);
                var ret = controller.RoleStart();
                if (ret)
                {
                    Logging.Write("NougakudoController:Initialized.", Logging.LogType.INFO);
                    controller.initialized = true;
                }
                else
                {
                    CleanupConfiguration();
                    Logging.Write("NougakudoController:Initialized error. Wait next times.", Logging.LogType.WARNING);
                    UploadLogfile();
                    return false;
                }
            }
            else
            {
                Logging.Write("NougakudoController:Not need Initialize.", Logging.LogType.INFO);
            }
            UploadLogfile();
            return true;
        }

        /// <summary>
        /// NougakuDoController Initialize for Role.OnStart event or first found configiration.xml.
        /// </summary>
        /// <returns></returns>
        bool RoleStart()
        {
            bool ret = false;

            var result = Commons.Runtime.CreateInstance();
            runtime = result.Item2;
            if (!result.Item1)
            {
                return false;
            }
            if (runtime.Downloads.Count != 0)
            {
                Downloader downloader = new Downloader(runtime);
                Logging.Write("NougakudoController:Download Start.", Logging.LogType.INFO);
                var downloadResult = downloader.Start();
                if (!downloadResult.Item1)
                {
                    Logging.Write("NougakudoController:Downloader error ={0}.", new object[] { downloadResult.Item2 }, Logging.LogType.ERROR);
                    UploadLogfile();
                    return false;
                }
                else
                    Logging.Write("NougakudoController:Download completed.", Logging.LogType.INFO);
            }
            UploadLogfile();

            Installer installer = new Installer(runtime);
            ret = installer.Start();
            if (!ret)
            {
                Logging.Write("NougakudoController:Install error.", Logging.LogType.ERROR);
                UploadLogfile();
                return false;
            }
            else
                Logging.Write("NougakudoController:Install completed.", Logging.LogType.INFO);
            UploadLogfile();

            Logging.Write("NougakudoController:Launcher Start.", Logging.LogType.INFO);
            launcher = new Launcher(runtime);
            ret = launcher.Run();
            UploadLogfile();
            return ret;
        }

        bool SetupWatcher()
        {
            try
            {
                var currentFolder = AppDomain.CurrentDomain.BaseDirectory;
                stopWatcher = new FileSystemWatcher(currentFolder, Commons.Constants.STOP_REQUEST);
                stopWatcher.Changed += new FileSystemEventHandler(stopWatcher_Changed);
                startWatcher = new FileSystemWatcher(currentFolder, Commons.Constants.START_REQUEST);
                startWatcher.Changed += new FileSystemEventHandler(startWatcher_Changed);
                reStartWatcher = new FileSystemWatcher(currentFolder, Commons.Constants.RESTART_REQUEST);
                reStartWatcher.Changed += new FileSystemEventHandler(reStartWatcher_Changed);
                controllerStopWatcher = new FileSystemWatcher(currentFolder, Commons.Constants.ROLE_STOP_REQUEST);
                controllerStopWatcher.Created += new FileSystemEventHandler(controllerStopWatcher_Created);
                downloadTimer = new System.Timers.Timer();
                downloadTimer.Elapsed += new System.Timers.ElapsedEventHandler(downloadTimer_Elapsed);
                downloadTimer.Interval = 1000.0 * runtime.Environment.Interval;

                StartAllWatcher();
                Logging.Write("NougakudoController:Watcher started.", Logging.LogType.INFO);
            }
            catch (Exception e)
            {
                Logging.Write("NougakudoController:Watcher Cretical error {0}.", new object[]{e.Message}, Logging.LogType.INFO);
                UploadLogfile();
                Environment.Exit(-1);
            }
            UploadLogfile();
            return true;
        }

        /// <summary>
        /// Start request event for NougakuDo.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void startWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            var ret = SetProcessingStart();
            if (!ret) return;
            StopAllWatcher();
            try
            {
                if (launcher != null) launcher.MaintenanceRunningProcess(); // Stable NougakudoLanucher behavior.
                Logging.Write("NougakudoController:StartWatcher start.", Logging.LogType.INFO);
                if (File.Exists(e.FullPath))
                {
                    var apps = File.ReadAllLines(e.FullPath);
                    File.Delete(e.FullPath);
                    if (!processing)
                    {
                        Logging.Write("NougakudoController:StartWatcher stop for RoleStop .", Logging.LogType.INFO);
                        return;
                    }
                    StartRequest(apps);
                }
            }
            catch (Exception ex)
            {
                Logging.Write("NougakudoController:StartWatcher error ={0}.", new object[] { ex.Message }, Logging.LogType.ERROR);
            }
            finally
            {
                ret = SetProcessingStop();
                StartAllWatcher();
                Logging.Write("NougakudoController:StartWatcher finish.", Logging.LogType.INFO);
                UploadLogfile();
            }
        }

        /// <summary>
        /// Stop request event for NougakuDo.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void stopWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            var ret = SetProcessingStart();
            if (!ret) return;
            StopAllWatcher();
            try
            {
                if (launcher != null) launcher.MaintenanceRunningProcess(); // Stable NougakudoLanucher behavior.
                Logging.Write("NougakudoController:StopWatcher start.", Logging.LogType.INFO);
                if (File.Exists(e.FullPath))
                {
                    var apps = File.ReadAllLines(e.FullPath);
                    File.Delete(e.FullPath);
                    if (!processing)
                    {
                        Logging.Write("NougakudoController:StopWatcher stop for RoleStop .", Logging.LogType.INFO);
                        return;
                    }
                    StopRequest(apps);
                }
            }
            catch (Exception ex)
            {
                Logging.Write("NougakudoController:StopWatcher error ={0}.", new object[] { ex.Message }, Logging.LogType.ERROR);
            }
            finally
            {
                ret = SetProcessingStop();
                StartAllWatcher();
                Logging.Write("NougakudoController:StopWatcher finish.", Logging.LogType.INFO);
                UploadLogfile();
            }
        }

        /// <summary>
        /// Re start event for NougakuDo.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void reStartWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            var ret = SetProcessingStart();
            if (!ret) return;
            StopAllWatcher();
            try
            {
                if (launcher != null) launcher.MaintenanceRunningProcess(); // Stable NougakudoLanucher behavior.
                Logging.Write("NougakudoController:RestartWatcher start.", Logging.LogType.INFO);
                if (File.Exists(e.FullPath))
                {
                    var apps = File.ReadAllLines(e.FullPath);
                    File.Delete(e.FullPath);
                    Logging.Write("NougakudoController:Nougakudo stop request.", Logging.LogType.INFO);
                    if (!processing)
                    {
                        Logging.Write("NougakudoController:RestartWatcher stop for RoleStop .", Logging.LogType.INFO);
                        return;
                    }
                    StopRequest(apps);
                    if (!processing)
                    {
                        Logging.Write("NougakudoController:RestartWatcher stop for RoleStop .", Logging.LogType.INFO);
                        return;
                    }
                    StartRequest(apps);
                }
            }
            catch (Exception ex)
            {
                Logging.Write("NougakudoController:RestartWatcher error ={0}.", new object[] { ex.Message }, Logging.LogType.ERROR);
            }
            finally
            {
                ret = SetProcessingStop();
                StartAllWatcher();
                Logging.Write("NougakudoController:RestartWatcher finish.", Logging.LogType.INFO);
                UploadLogfile();
            }

        }

        void StopRequest(string[] apps)
        {
#if DEBUG
            Logging.Write("NougakudoController:Nougakudo stop request.", Logging.LogType.INFO);
#endif
            if (apps == null)
                launcher.RequestStop();
            else
                launcher.RequestStop(apps);

        }

        void StartRequest(string[] apps)
        {
#if DEBUG
            Logging.Write("NougakudoController:Launcher Start.", Logging.LogType.INFO);
#endif
            launcher.Run(apps);
        }

        /// <summary>
        /// Stop request event for NougakuDoContoller.
        /// This occurs when Role.OnStop evnet.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void controllerStopWatcher_Created(object sender, FileSystemEventArgs e)
        {
            lock (syncObj)
            {
                processing = false;
            }
            StopAllWatcher();
            try
            {
                Logging.Write("NougakudoController:CotrollerStopWatcher start.", Logging.LogType.INFO);
                if (File.Exists(e.FullPath))
                {
                    File.Delete(e.FullPath);
                    Logging.Write("NougakudoController:Nougakudo role stop request.", Logging.LogType.INFO);
                    StopRequest(null);
                }
            }
            catch (Exception ex)
            {
                Logging.Write("NougakudoController:ControllerStopWatcher error ={0}.", new object[] { ex.Message }, Logging.LogType.ERROR);
            }
            finally
            {
                Logging.Write("NougakudoController:ControllerStopWatcher finish.", Logging.LogType.INFO);
            }
            UploadLogfile();
            //Logging.Write("NougakudoController:Stop.", Logging.LogType.INFO);
            System.Environment.Exit(0);

        }

        /// <summary>
        /// Timer event for polling new version or first found configuration.xaml.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void downloadTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            var ret = SetProcessingStart();
            if (!ret) return;
            StopAllWatcher();
            try
            {
                Logging.Write("NougakudoController:DownloaderTimer start.", Logging.LogType.INFO);

                if (!controller.initialized)
                {
                    ret = controller.ControllerStart();
                }
                else
                {
                    UpdateProcess();
                }

            }
            catch (Exception ex)
            {
                Logging.Write("NougakudoController:DownloaderTimer error ={0}.", new object[] { ex.Message }, Logging.LogType.ERROR);
            }
            finally
            {
                ret = SetProcessingStop();
                StartAllWatcher();
                Logging.Write("NougakudoController:DownloadTimer finish.", Logging.LogType.INFO);
                UploadLogfile();
            }
        }

        void UpdateProcess()
        {
            if (launcher != null) launcher.MaintenanceRunningProcess(); // Stable NougakudoLanucher behavior.
            Logging.Write("NougakudoController:DownloadTimer start update process .", Logging.LogType.INFO);
            Downloader downloader = new Downloader(runtime);
            bool all = false;   // check updated nougakudo
            bool unClearConfig = false;
            if (!processing)
            {
                Logging.Write("NougakudoController:DownloadTimer stop for RoleStop .", Logging.LogType.INFO);
                return;
            }
            var downloadResult = downloader.GetConfiguration(Commons.Constants.NEW_CONFIG);
            bool ret = downloadResult.Item1;
            if (ret)
            {
                Logging.Write("NougakudoController:Download completed for {0}.", new object[] { Commons.Constants.NEW_CONFIG }, Logging.LogType.INFO);
                File.Copy(Path.Combine(runtime.Environment.DownloadFolder, Commons.Constants.NEW_CONFIG),
                          Commons.Constants.NEW_CONFIG, true);
                ret = runtime.LoadNewConfiguration();
                unClearConfig = ret;
                // success download configuration.xml and no change as no download list. 
                if (ret && runtime.Downloads.Count == 0)
                    ret = false;
            }
            UploadLogfile();
            if (!processing)
            {
                Logging.Write("NougakudoController:DownloadTimer stop for RoleStop .", Logging.LogType.INFO);
                return;
            }
            if (ret)
            {
                Logging.Write("NougakudoController:Download start.", Logging.LogType.INFO);
                downloadResult = downloader.Start();
                ret = downloadResult.Item1;
                if (!ret)
                    unClearConfig = false;
            }
            if (!processing)
            {
                Logging.Write("NougakudoController:DownloadTimer stop for RoleStop .", Logging.LogType.INFO);
                return;
            }
            if (ret)
            {
                all = runtime.DownloadableRuntime;  // check include updated nougakudo.
                if (all)
                {
                    Logging.Write("NougakudoController:All stop request start for update nougakudo.", Logging.LogType.INFO);
                    StopRequest(null);
                    if (!processing)
                    {
                        Logging.Write("NougakudoController:StopWatcher stop for RoleStop .", Logging.LogType.INFO);
                        return;
                    }
                }
                else
                    StopRequest(runtime.Downloads.ToArray());
                if (!processing)
                {
                    Logging.Write("NougakudoController:DownloadTimer stop for RoleStop .", Logging.LogType.INFO);
                    return;
                }
                Logging.Write("NougakudoController:Installer start.", Logging.LogType.INFO);
                UploadLogfile();
                Installer installer = new Installer(runtime);
                ret = installer.Start();
                if (!ret)
                {
                    // Clean up, if insutall failed.
                    //Logging.Write("NougakudoController:Installer cleanup.", Logging.LogType.ERROR);
                    UploadLogfile();
                    unClearConfig = false;
                }
            }
            if (!processing)
            {
                Logging.Write("NougakudoController:DownloadTimer stop for RoleStop .", Logging.LogType.INFO);
                return;
            }
            if (ret)
            {
                Logging.Write("NougakudoController:Launcher Start.", Logging.LogType.ERROR);
                if (all)
                {
                    Logging.Write("NougakudoController:All start request start for update nougakudo.", Logging.LogType.INFO);
                    launcher.Run();
                }
                else
                    launcher.Run(runtime.Downloads.ToArray());
            }
            else
            {
                // Cleanup configuration.xml
                if (!unClearConfig)
                {
                    StopRequest(null);
                    CleanupConfiguration();
                    controller.initialized = false;
                }
            }
            UploadLogfile();
        }

        bool SetProcessingStart()
        {
            bool ret = false;
            lock (syncObj)
            {
                if (!processing)
                {
                    processing = true;
                    ret = true;
                }
            }
            return ret;
        }

        bool SetProcessingStop()
        {
            bool ret = false;
            lock (syncObj)
            {
                if (processing)
                {
                    processing = false;
                    ret = true;
                }
            }
            return ret;
        }

        void StopAllWatcher()
        {
            startWatcher.EnableRaisingEvents = false;
            stopWatcher.EnableRaisingEvents = false;
            reStartWatcher.EnableRaisingEvents = false;
            downloadTimer.Enabled = false;
        }

        void StartAllWatcher()
        {
            startWatcher.EnableRaisingEvents = true;
            stopWatcher.EnableRaisingEvents = true;
            reStartWatcher.EnableRaisingEvents = true;
            downloadTimer.Enabled = true;

            controllerStopWatcher.EnableRaisingEvents = true;
        }

        // Upload log file to blob storage.
        bool UploadLogfile()
        {
            var loginfo = Logging.GetLogFileName();
            var ret = NougakudoCompanion.Commons.BlobStorageHelper.UploadBlob(
                            runtime.Environment, 
                            loginfo.Item2 + "-" + runtime.Environment.RoleInstanceID, 
                            Path.Combine(loginfo.Item1, loginfo.Item2));
            if (!ret.Item1)
            {
                Logging.Write("NougakudoController:Log upload error {0}.", new object[]{ ret.Item2 },  Logging.LogType.ERROR);
            }
            return ret.Item1;
        }

        /// <summary>
        /// Cleanup configuration.xml.
        /// </summary>
        void CleanupConfiguration()
        {
            Logging.Write("NougakudoController:Cleanup configuration.xml.", Logging.LogType.INFO);
            Commons.BlobStorageHelper.DeleteBlob(runtime.Environment, Commons.Constants.CONFIGURATION);
            DeleteFile(Commons.Constants.CONFIGURATION);
            DeleteFile(Commons.Constants.CONFIGURATION_BACKUP);
            DeleteFile(Commons.Constants.NEW_CONFIG);
            var file = Path.Combine(runtime.Environment.DownloadFolder, Commons.Constants.CONFIGURATION);
            DeleteFile(file);
            file = Path.Combine(runtime.Environment.DownloadFolder, Commons.Constants.NEW_CONFIG);
            DeleteFile(file);

        }

        void DeleteFile(string file)
        {
            try
            {
                if (File.Exists(file))
                    File.Delete(file);
            }
            catch
            {
            }
        }

    }
}
